package top.yamhk.agent.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.json.JSONUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 拷贝工具类
 *
 * @author 庆 HongKong
 * @date 2019/11/9 10:40
 * //ToString-StackOverflowError
 */
@Slf4j
public class BeanCopyUtils {

    /**
     * 私有构造
     */
    private BeanCopyUtils() {

    }

    /**
     * 拷贝
     *
     * @param o     o
     * @param clazz clazz
     * @param <T>   <T>
     * @return T
     */
    public static <T> T copyByFastJson(Object o, Class<T> clazz) {
        return null;
    }

    /**
     * 拷贝-StackOverflowError
     *
     * @param o     o
     * @param clazz clazz
     * @param <T>   <T>
     * @return <T>
     */
    public static <T> T copyByGson(Object o, Class<T> clazz) {

        try {
//            String toString = GSON.toJson(o);
//            return GSON.fromJson(toString, clazz);
            return JSONUtil.toBean(JSONUtil.toJsonStr(o), clazz);
        } catch (Exception e) {
//            return GSON.fromJson(o.toString(), clazz);
            try {
                return JSONUtil.toBean((String) o, clazz);
            } catch (Exception ex) {
                log.error("序列化失败");
                return null;
            }
        }
    }

    /**
     * 拷贝-在实际开发中，BeanUtils使用更普遍一点，状错误的概率更低一点。
     *
     * @param source source
     */
    public static <T> T copyBySpring(Object source, Class<T> clazz) {
        T target;
        try {
            target = clazz.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
            return null;
        }
        BeanUtils.copyProperties(source, target);
        return target;
    }

    /**
     * 拷贝
     *
     * @param source source
     */
    public static <T> T copyByApache(Object source, Class<T> clazz) {
        T target = null;
        try {
            target = clazz.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
            return null;
        }
        BeanUtils.copyProperties(source, target);
        return target;
    }

    /**
     * 拷贝
     *
     * @param source source
     */
    public static <T> T copyByHuTool(Object source, Class<T> clazz) {
        T target = null;
        try {
            target = clazz.getConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
            return null;
        }
        CopyOptions copyOptions = CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true);
        BeanUtil.copyProperties(source, target, copyOptions);
        return target;
    }

    /**
     * 拷贝
     *
     * @param source source
     * @param target target
     */
    public static void copyProperties(Object source, Object target) {
        //cn.hutool.core.bean
        CopyOptions copyOptions = CopyOptions.create().setIgnoreNullValue(true).setIgnoreError(true);
        BeanUtil.copyProperties(source, target, copyOptions);
    }

    @SneakyThrows
    public static Map<String, Object> transBean2Map(Object obj) {
        if (obj == null) {
            return null;
        }
        Map<String, Object> map = new HashMap<>(5);
        BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
        PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
        for (PropertyDescriptor property : propertyDescriptors) {
            String key = property.getName();
            if (!"class".equals(key)) {
                Method getter = property.getReadMethod();
                Object value = getter.invoke(obj);
                map.put(key, value);
            }
        }
        return map;
    }
}
